// SPDX-License-Identifier: GPL-2.0
/*  Himax Android Driver Sample Code for hx83193 chipset
 *
 *  Copyright (C) 2021 Himax Corporation.
 *
 *  This software is licensed under the terms of the GNU General Public
 *  License version 2,  as published by the Free Software Foundation,  and
 *  may be copied,  distributed,  and modified under those terms.
 *
 *  This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#define hx83193_data_adc_num 120
#include "himax_platform.h"
#include "himax_common.h"
#include "himax_ic_core.h"

static void hx83193_sense_on(uint8_t FlashMode)
{
	uint8_t tmp_data[DATA_LEN_4];
	int retry = 0;
	int ret = 0;

	I("%s Enter flashMode = 0x%x \n", __func__,FlashMode);
	himax_mcu_interface_on();

	if (!FlashMode) {
#if (HX_RST_PIN_FUNC == 0x01)
		himax_mcu_ic_reset(false, false);
#elif (HX_RST_PIN_FUNC == 0x02)
		/* Need Customer do TP reset pin */
#else
		himax_mcu_system_reset();
#endif
	} else {
		do {
			himax_parse_assign_cmd(data_clear, tmp_data,
					       sizeof(tmp_data));
			himax_mcu_register_write(addr_ctrl_fw, DATA_LEN_4,
						 tmp_data);

			usleep_range(20000, 21000);

			himax_mcu_register_read(addr_ctrl_fw, DATA_LEN_4,
						tmp_data);

			I("%s:Read status from IC = %X,%X\n", __func__,
			  tmp_data[0], tmp_data[1]);
		} while (tmp_data[0] != 0x00 && retry++ < 5);

		if (retry >= 5) {
			E("%s: Fail:\n", __func__);
#if (HX_RST_PIN_FUNC == 0x01)
			himax_mcu_ic_reset(false, false);
#elif (HX_RST_PIN_FUNC == 0x02)
			/* Need Customer do TP reset pin */
#else
			himax_mcu_system_reset();
#endif
		} else {
			/* reset code*/
			tmp_data[0] = 0x00;

			ret = himax_bus_write(addr_sense_on_off_0, tmp_data, 1,
					      HIMAX_I2C_RETRY_TIMES);
			if (ret < 0)
				E("%s: i2c access fail!\n", __func__);

			usleep_range(20000, 21000);

			ret = himax_bus_write(addr_sense_on_off_1, tmp_data, 1,
					      HIMAX_I2C_RETRY_TIMES);
			if (ret < 0)
				E("%s: i2c access fail!\n", __func__);
		}
		usleep_range(20000, 21000);

#if defined(HIMAX_I2C_PLATFORM)
		ret = himax_bus_read(addr_AHB_rdata_byte_0, tmp_data,
				     DATA_LEN_4, HIMAX_I2C_RETRY_TIMES);
		if (ret < 0)
			E("%s: i2c access fail!\n", __func__);
#endif
	}
}

static bool hx83193_sense_off(void)
{
	bool result = true;
	uint8_t cnt = 0;
	uint8_t tmp_data[DATA_LEN_4] = { 0 };
	uint8_t cMax = 14;
	uint8_t check = 0x87;
	int ret = 0;

	usleep_range(20000, 21000);
	I("%s:addr_cs_central_state = 0x%x \n", __func__,addr_cs_central_state);
	himax_mcu_register_read(addr_cs_central_state, DATA_LEN_4, tmp_data);
	I("%s:tmp_data[0]= 0x%x \n", __func__,tmp_data[0]);
	if (tmp_data[0] != 0x0C) {
		do {
			tmp_data[3] = 0x00;
			tmp_data[2] = 0x00;
			tmp_data[1] = 0x00;
			tmp_data[0] = 0xA5;
			
			I("%s:addr_ctrl_fw = 0x%x \n", __func__,addr_ctrl_fw);
			himax_mcu_register_write(addr_ctrl_fw, DATA_LEN_4,
						 tmp_data);

			usleep_range(20000, 21000);
			himax_mcu_register_read(addr_ctrl_fw, DATA_LEN_4,
						tmp_data);
			if (cnt++ >= cMax)
				break;
		} while (tmp_data[0] != check);
		I("%s: 9000005C data[0]=%X, Retry times = %d\n", __func__,
		  tmp_data[0], cnt);
	}

	do {
		tmp_data[0] = para_sense_off_0;
		I("%s:para_sense_off_0= 0x%x \n", __func__,para_sense_off_0);
		ret = himax_bus_write(addr_sense_on_off_0, tmp_data, 1,
				      HIMAX_I2C_RETRY_TIMES);
		if (ret < 0) {
			E("%s: i2c access fail!\n", __func__);
			return false;
		}

		tmp_data[0] = para_sense_off_1;
		
		I("%s:para_sense_off_1= 0x%x \n", __func__,para_sense_off_1);
		ret = himax_bus_write(addr_sense_on_off_1, tmp_data, 1,
				      HIMAX_I2C_RETRY_TIMES);
		if (ret < 0) {
			E("%s: i2c access fail!\n", __func__);
			return false;
		}
	
		I("%s:register read addr_cs_central_state= 0x%x \n", __func__,addr_cs_central_state);
		himax_mcu_register_read(addr_cs_central_state, DATA_LEN_4,
					tmp_data);
		I("%s: Check enter_save_mode data[0]=%X\n", __func__,tmp_data[0]);

		if (tmp_data[0] == 0x0C) {
			I("%s: tmp_data[0] == 0x0C\n", __func__);
			return true;
		} else if (cnt == 6) {
			
			I("%s: cnt == 6\n", __func__);
			usleep_range(10000, 11000);
#if (HX_RST_PIN_FUNC == 0x01)
			himax_mcu_ic_reset(false, false);
#elif (HX_RST_PIN_FUNC == 0x02)
			/* Need Customer do TP reset pin */
#else
			himax_mcu_system_reset();
#endif
		}

	} while (cnt++ < 15);

	return result;
}

static void hx83193_mcu_flash_dump_func(uint8_t local_flash_command,
					int Flash_Size, uint8_t *flash_buffer)
{
	uint8_t tmp_data[DATA_LEN_4] = { 0 };
	int page_prog_start = 0;
	uint8_t Original_speed[DATA_LEN_4] = { 0 };

	I("%s,Entering\n", __func__);

	/* disable retry wrapper command*/
	tmp_data[0] = 0xA5;
	himax_mcu_register_write(addr_retry_wrapper_clr_pw, 4, tmp_data);

	hx83193_sense_off();

	/* ===Get Flash Speed===*/
	himax_mcu_register_read(addr_spi200_flash_speed, DATA_LEN_4,
				Original_speed);

	/* ===Set Flash Speed===*/
	himax_parse_assign_cmd(data_set_flash_speed, tmp_data,
			       sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_flash_speed, DATA_LEN_4, tmp_data);

	himax_mcu_burst_enable(1);

	for (page_prog_start = 0; page_prog_start < Flash_Size;
	     page_prog_start += 256) {
		himax_mcu_register_read(page_prog_start, 256,
					&flash_buffer[page_prog_start]);
	}

	/* ===Set Flash Speed===*/
	himax_mcu_register_write(addr_spi200_flash_speed, DATA_LEN_4,
				 Original_speed);

	hx83193_sense_on(0x01);
}

static void hx83193_mcu_flash_programming(uint8_t *FW_content, int start_addr,
					  int length)
{
	int page_prog_start = 0;
	uint8_t tmp_data[DATA_LEN_4] = { 0 };
	uint8_t buring_data[FLASH_RW_MAX_LEN]; /* Read for flash data, 128K*/
	uint8_t Original_speed[DATA_LEN_4] = { 0 };

	I("%s", __func__);
	/* ===Get Flash Speed===*/
	himax_mcu_register_read(addr_spi200_flash_speed, DATA_LEN_4,
				Original_speed);

	/* ===Set Flash Speed===*/
	himax_parse_assign_cmd(data_set_flash_speed, tmp_data,
			       sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_flash_speed, DATA_LEN_4, tmp_data);

	himax_mcu_interface_on();

	/* ===SPI TX-FIFO Reset===*/
	himax_parse_assign_cmd(data_spi200_txfifo_rst, tmp_data,
			       sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_fifo_rst, DATA_LEN_4, tmp_data);

	/* ===SPI Format===*/
	himax_parse_assign_cmd(data_spi200_trans_fmt, tmp_data,
			       sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_trans_fmt, DATA_LEN_4, tmp_data);

	for (page_prog_start = start_addr;
	     page_prog_start < start_addr + length;
	     page_prog_start += FLASH_RW_MAX_LEN) {
		/* ===Flash Write Enable ===*/
		himax_parse_assign_cmd(data_spi200_trans_ctrl_2, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_trans_ctrl, DATA_LEN_4,
					 tmp_data);

		himax_parse_assign_cmd(data_spi200_cmd_2, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_cmd, DATA_LEN_4, tmp_data);

		/* ===WEL Write Control ===*/
		himax_parse_assign_cmd(data_spi200_trans_ctrl_6, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_trans_ctrl, DATA_LEN_4,
					 tmp_data);

		himax_parse_assign_cmd(data_spi200_cmd_1, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_cmd, DATA_LEN_4, tmp_data);

		himax_mcu_register_read(addr_spi200_data, DATA_LEN_4, tmp_data);
		/* === Check WEL Fail ===*/
		if (((tmp_data[0] & 0x02) >> 1) == 0) {
			I("%s:SPI 0x8000002c = %d\n", __func__, tmp_data[0]);
			break;
		}

		/*Set 256 Bytes Page Write*/
		himax_parse_assign_cmd(data_spi200_trans_ctrl_4, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_trans_ctrl, DATA_LEN_4,
					 tmp_data);

		memset(tmp_data, 0x00, sizeof(tmp_data));
		tmp_data[3] = (uint8_t)(page_prog_start >> 24);
		tmp_data[2] = (uint8_t)(page_prog_start >> 16);
		tmp_data[1] = (uint8_t)(page_prog_start >> 8);
		tmp_data[0] = (uint8_t)page_prog_start;
		himax_mcu_register_write(addr_spi200_addr, DATA_LEN_4,
					 tmp_data);

		memset(buring_data, 0x00, sizeof(buring_data));
		himax_parse_assign_cmd(addr_spi200_data, buring_data,
				       ADDR_LEN_4);

		memcpy((buring_data + ADDR_LEN_4),
		       (FW_content + (page_prog_start - start_addr)), 16);
		if (himax_bus_write(addr_AHB_address_byte_0, buring_data,
				    (ADDR_LEN_4 + 16),
				    HIMAX_I2C_RETRY_TIMES) < 0) {
			E("%s: i2c access fail!\n", __func__);
			return;
		}
		/*Write Command: PP*/
		himax_parse_assign_cmd(data_spi200_cmd_6, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_cmd, DATA_LEN_4, tmp_data);

		memcpy((buring_data + ADDR_LEN_4),
		       (FW_content + (page_prog_start - start_addr + 16)), 240);

		if (himax_bus_write(addr_AHB_address_byte_0, buring_data,
				    (ADDR_LEN_4 + 240),
				    HIMAX_I2C_RETRY_TIMES) < 0) {
			E("%s: i2c access fail!\n", __func__);
			return;
		}

		if (!himax_mcu_wait_wip(1))
			E("%s:Flash_Programming Fail\n", __func__);
	}
	/* ===Set Flash Speed===*/
	himax_mcu_register_write(addr_spi200_flash_speed, DATA_LEN_4,
				 Original_speed);
}

static bool hx83193_mcu_dd_clk_set(bool enable)
{
	uint8_t data[DATA_LEN_4] = { 0 };
	uint8_t check_STOP_FW = 0x6A;
	uint8_t cMax = 7;
	uint8_t cnt = 0;

	if (enable) {
		do {
			data[0] = 0x5A;
			himax_mcu_register_write(addr_ctrl_fw, DATA_LEN_4,
						 data);

			usleep_range(20000, 21000);
			himax_mcu_register_read(addr_ctrl_fw, DATA_LEN_4, data);
			I("%s: Check 9000005C data[0]=%X\n", __func__, data[0]);
			if (cnt++ >= cMax)
				break;
		} while (data[0] != check_STOP_FW);
		if (data[0] == check_STOP_FW)
			I("%s: STOP_FW_BY_HOST finished!\n", __func__);
		else
			W("%s: STOP_FW_BY_HOST Fail!\n", __func__);
	} else {
		do {
			data[0] = 0x00;
			himax_mcu_register_write(addr_ctrl_fw, DATA_LEN_4,
						 data);

			usleep_range(20000, 21000);
			himax_mcu_register_read(addr_ctrl_fw, DATA_LEN_4, data);
			I("%s: Check 9000005C data[0]=%X\n", __func__, data[0]);
			if (cnt++ >= cMax)
				break;
		} while (data[0] == check_STOP_FW);
		if (data[0] != check_STOP_FW)
			I("%s: START_FW_BY_HOST finished!\n", __func__);
		else
			E("%s: START_FW_BY_HOST Fail!\n", __func__);
	}

	data[0] = (enable) ? 0xDD : 0x00;
	return (himax_mcu_register_write(addr_osc_en, DATA_LEN_4, data) ==
		NO_ERR);
}

static void hx83193_mcu_dd_reg_en(bool enable)
{
	uint8_t data[DATA_LEN_4] = { 0 };

	data[0] = 0xA5;
	data[1] = 0x00;
	data[2] = 0x00;
	data[3] = 0x00;

	himax_mcu_register_write(addr_osc_pw, DATA_LEN_4, data);
	data[0] = 0x00;
	data[1] = 0x55;
	data[2] = 0x66;
	data[3] = 0xCC;

	himax_mcu_dd_reg_write(0xEB, 0, 4, data, 0);
	data[0] = 0x00;
	data[1] = 0x83;
	data[2] = 0x19;
	data[3] = 0x2A;
	himax_mcu_dd_reg_write(0xB9, 0, 4, data, 0);
}

static void hx83193_func_re_init(void)
{
	g_core_fp.fp_sense_on = hx83193_sense_on;
	g_core_fp.fp_sense_off = hx83193_sense_off;
	g_core_fp.fp_dd_clk_set = hx83193_mcu_dd_clk_set;
	g_core_fp.fp_dd_reg_en = hx83193_mcu_dd_reg_en;
	g_core_fp.fp_flash_dump_func = hx83193_mcu_flash_dump_func;
	g_core_fp.fp_flash_programming = hx83193_mcu_flash_programming;
}

static bool hx83193_chip_detect(void)
{
	uint8_t tmp_data[DATA_LEN_4] = { 0 };
	bool ret_data = false;
	int ret = 0;
	int i = 0;
	I("%s\n", __func__);
	ret = himax_mcu_in_cmd_struct_init();
	if (ret < 0) {
		ret_data = false;
		E("%s:cmd_struct_init Fail:\n", __func__);
		return ret_data;
	}

	hx83193_func_re_init();

	hx83193_sense_off();

	for (i = 0; i < 5; i++) {
		himax_mcu_register_read(addr_icid_addr, DATA_LEN_4, tmp_data);
		I("%s:Read driver IC ID = HX%X%X%X\n", __func__, tmp_data[3],
		  tmp_data[2], tmp_data[1]);

		if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x19) &&
		    (tmp_data[1] == 0x3a)) {
			strlcpy(private_ts->chip_name, HX_83193A_SERIES_PWON,
				30);

			ret_data = true;
			(ic_data)->ic_adc_num = hx83193_data_adc_num;

			return ret_data;
		}
	}
	ret_data = false;
	E("%s:Read driver ID register Fail:\n", __func__);
	E("Could NOT find Himax Chipset\n");
	E("Please check 1.VCCD,VCCA,VSP,VSN\n");
	E("2.LCM_RST,TP_RST\n");
	E("3.Power On Sequence\n");

	return ret_data;
}

bool _hx83193_init(void)
{
	bool ret = false;

	I("%s\n", __func__);
	ret = hx83193_chip_detect();
	return ret;
}
